On Go DNS Resolution
kd@ 3/19/2022

A very silly minor detail

I was trying to bash out a little manual DNS client in Go because it’s so much better at timeouts than Python. No big deal-

package main

import (
    "context"
    "fmt"
    "net"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Second*5))
    defer cancel()

    r := &net.Resolver{
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            return (&net.Dialer{
                Timeout: time.Duration(time.Second),
            }).DialContext(ctx, "udp", "1.2.3.4:53") // 1.2.3.4 is not a real DNS server
        },
    }
    ips, err := r.LookupHost(ctx, "www.google.com")
    fmt.Println(ips, err)
}

Unfortunately, I was getting a result here, instead of a timeout. Extremely confusing. There were some notes in the documentation about the pure-go resolver versus the system libc resolver, but it all sounded like the pure-go version was preferred, so surely this wasn’t something crazy like falling back to a real working DNS server, right?

No that’s totally it- you need to set PreferGo: true in your Resolver setup, and then you can check what the specified server returned, not some sort of sum of your local DNS cache and the server you’re trying to talk to.

Full, working version-

package main

import (
    "context"
    "fmt"
    "net"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Second*5))
    defer cancel()

    r := &net.Resolver{
        PreferGo: true,
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            return (&net.Dialer{
                Timeout: time.Duration(time.Second),
            }).DialContext(ctx, "udp", "1.2.3.4:53")
        },
    }
    ips, err := r.LookupHost(ctx, "www.google.com")
    fmt.Println(ips, err)
}